#include "utils.h"
template <typename T> bool valueIsSame(T a, T b, const T tol) {
    return std::abs(a - b) <= tol;
}

template <typename T> bool valueIsSame(T a, T b) { return a == b; }
template <typename T> bool valuesIsSame(T *a, T *b, const int N, const T tol) {
    bool flag = true;
    for (int i = 0; i < N; i++) {
        if (!valueIsSame(a[i], b[i], tol)) {
            flag = false;
            break;
        }
    }
    return flag;
}

template <typename T> bool valuesIsSame(T *a, T *b, const int N) {
    bool flag = true;
    for (int i = 0; i < N; i++) {
        if (!valueIsSame(a[i], b[i])) {
            flag = false;
            break;
        }
    }
    return flag;
}
template <typename T>
bool valuesIsSame(vector<vector<T>> a, vector<vector<T>> b, const T tol) {
    if (a.empty() && b.empty())
        return true;
    int a_rows = a.size();
    int b_rows = b.size();
    int flag = true;
    int a_cols = a[0].size();
    int b_cols = b[0].size();
    if (a_cols == b_cols && a_cols == 0)
        return flag;
    for (int r = 0; r < a_rows; r++) {
        for (int c = 0; c < a_cols; c++) {
            if (abs(a[r][c] - b[r][c]) > tol) {
                return false;
            }
        }
    }
    return flag;
}

template <typename T>
bool valuesIsSame(vector<vector<T>> a, vector<vector<T>> b) {
    if (a.empty() && b.empty())
        return true;
    int a_rows = a.size();
    int b_rows = b.size();
    int flag = true;
    int a_cols = a[0].size();
    int b_cols = b[0].size();
    if (a_cols == b_cols && a_cols == 0)
        return flag;
    for (int r = 0; r < a_rows; r++) {
        for (int c = 0; c < a_cols; c++) {
            if (a[r][c] != b[r][c]) {
                return false;
            }
        }
    }
    return flag;
}
template <typename T>
bool valuesIsSame(const vector<T> &a, const vector<T> &b, const T tol) {
    if (a.empty() ^ b.empty())
        return false;
    if (a.size() != b.size())
        return false;
    bool flag = true;
    for (int i = 0; i < a.size(); i++) {
        if (std::abs(a[i] - b[i]) > tol) {
            flag = false;
            break;
        }
    }
    return flag;
}

template <typename T>
bool valuesIsSame(const vector<T> &a, const vector<T> &b) {
    if (a.empty() ^ b.empty())
        return false;
    if (a.size() != b.size())
        return false;
    bool flag = true;
    for (int i = 0; i < a.size(); i++) {
        if (a[i] != b[i]) {
            flag = false;
            break;
        }
    }
    return flag;
}
template <typename T>
void randint(const int start, const int end, std::vector<T> &cache) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> distrib(start, end);
    const int N = cache.size();
    for (int i = 0; i < N; i++)
        cache[i] = distrib(gen);
}
template bool valueIsSame<float>(float, float, const float);
template bool valueIsSame<double>(double, double, const double);
template bool valuesIsSame<float>(float *, float *, const int N, const float);
template bool valuesIsSame<double>(double *, double *, const int N,
                                   const double);

template bool valueIsSame<int>(int, int);
template bool valueIsSame<short>(short, short);
template bool valuesIsSame<int>(int *, int *, const int N);
template bool valuesIsSame<short>(short *, short *, const int N);

template bool valuesIsSame(vector<vector<int>> a, vector<vector<int>> b,
                           const int tol);

template bool valuesIsSame(vector<vector<char>> a, vector<vector<char>> b);
template bool valuesIsSame(vector<vector<int>> a, vector<vector<int>> b);
template bool valuesIsSame<int>(const vector<int> &, const vector<int> &);
template bool valuesIsSame<char>(const vector<char> &, const vector<char> &);

template void randint(const int start, const int end, std::vector<int> &cache);
